package com.glodon.paas.storage.swift;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpException;

import com.glodon.iaas.swift.FilesClient;
import com.glodon.iaas.swift.exception.FilesNotFoundException;
import com.glodon.iaas.swift.javabean.BucketInfo;
import com.glodon.iaas.swift.javabean.GetObjectRequest;
import com.glodon.iaas.swift.javabean.ObjectMetadata;
import com.glodon.iaas.swift.javabean.SwiftObject;
import com.glodon.paas.exception.PaasServerExceptionFactory;
import com.glodon.paas.storage.AbstractBulkStorage;
import com.glodon.paas.storage.BulkStorageException;
import com.glodon.paas.storage.InitResult;
import com.glodon.paas.storage.Part;
import com.glodon.paas.storage.ReadResult;
import com.glodon.paas.storage.WriteResult;

/**
 * An swift implementation for BulkStorage
 * 
 * @author chenyalei
 * 
 */
public class SwiftBulkStorage extends AbstractBulkStorage {
	private static final Object LOCK = new Object();

	private String bucketName;
	private FilesClient client;

	public SwiftBulkStorage(String keystoneEndpoint, String swiftEndpoint, String tenantName, String username,
			String password, String bucketName) throws IOException,
			HttpException {
		client = new FilesClient(keystoneEndpoint, swiftEndpoint, tenantName, username, password);
		client.login();
		this.bucketName = bucketName;
	}

	/**
	 * 支持断点下载功能
	 * 
	 * @return true
	 */
	public boolean isPartialReadSupported() {
		return true;
	}

	/**
	 * 支持断点上传功能
	 * 
	 * @return true
	 */
	public boolean isMultiPartUploadSupported() {
		return true;
	}

	/**
	 * 初始化断点上传
	 * 
	 * @param key
	 * @return MultiPartUploadHandle
	 * @throws BulkStorageException
	 */
	public InitResult initMultiPartUpload(String key)
			{
		try {
			InitResult handle = new InitResult(
					key);
			return handle;
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to initiate multipart Upload object to swift: KEY = "
							+ key, er);
		}
	}

	/**
	 * 分割上传文件
	 * 
	 * @param handle
	 * @param part
	 * @param is
	 * @return WriteResult
	 * @throws BulkStorageException
	 */
	public WriteResult uploadPart(InitResult handle, Part part,
			InputStream is) {
		this.ensureBucket(client, bucketName.concat("_segments"));
		try {
			InitResult swiftHandle = (InitResult) handle;
			String objPath = String.format("%s/%08d",
					swiftHandle.getManifest(), part.getIndex());
			String eTag = client.putObject(bucketName.concat("_segments"), is,
					swiftHandle.getContentType(), objPath, null);

			return new WriteResult(swiftHandle.getKey(), eTag);
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to create swift object group: KEY = "
							+ handle.getKey(), er);
		}
	}

	/**
	 * 删除上传失败的块文件
	 * 
	 * @param handle
	 * @throws BulkStorageException
	 */
	public void abortMultiPartUpload(InitResult handle)
			{
		try {
			InitResult swiftHandle = (InitResult) handle;
			try {
				// 删除已上传成功的分块文件
				client.getObjectMetaData(bucketName, swiftHandle.getKey());
				client.deleteObject(bucketName, swiftHandle.getKey());
			} catch (FilesNotFoundException e) {
				// 删除未上传完的分块文件
				ArrayList<SwiftObject> objects = (ArrayList<SwiftObject>) client
						.listObjects(bucketName.concat("_segments"),
								swiftHandle.getKey(), null, -1, null);
				System.out.println(objects.size());
				for (SwiftObject obj : objects) {
					client.deleteObject(bucketName.concat("_segments"), obj
							.getName());
				}
			}
		} catch (Exception er) {
			System.out.println(er.getMessage());
			throw PaasServerExceptionFactory.storageException(
					"failed to abortt Upload object to swift: KEY = "
							+ handle.getKey(), er);
		}
	}

	/**
	 * 上传part清单
	 * 
	 * @param handle
	 * @param parts
	 * @return WriteResult
	 * @throws BulkStorageException
	 */
	public WriteResult completeMultiPartUpload(InitResult handle,
			List<Part> parts) {
		this.ensureBucket(client, bucketName);
		try {
			InitResult swiftHandle = (InitResult) handle;
			String eTag = client.createManifestObject(bucketName, swiftHandle
					.getContentType(), swiftHandle.getKey(), String.format(
					"%s_segments/%s/", bucketName, swiftHandle.getManifest()),
					null, null);
			return new WriteResult(swiftHandle.getKey(), eTag);
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to create swift object group: KEY = "
							+ handle.getKey(), er);
		}
	}

	/**
	 * 上传单个文件
	 * 
	 * @param key
	 * @param object
	 * @return WriteResult
	 * @throws BulkStorageException
	 */
	public WriteResult write(String key, InputStream object, long length)
			{
		this.ensureBucket(client, bucketName);
		try {
			String eTag = client.putObject(bucketName, object, key, null);
			return new WriteResult(key, eTag);
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to write object to swift: KEY = " + key, er);
		}
	}

	/**
	 * 下载单个文件
	 * 
	 * @param key
	 * @return ReadResult
	 * @throws BulkStorageException
	 */
	public ReadResult read(String key) {
		GetObjectRequest request = new GetObjectRequest(bucketName, key);
		try {
			SwiftObject result = client.getObject(request);
			return new ReadResult(result.getObjectContent(), Long
					.valueOf(result.getObjectMetadata().getContentLength()));
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to read object from swift: KEY = " + key, er);
		}
	}

	/**
	 * 获取对象的metadata信息，可以用来验证对象是否存在
	 * 
	 * @param bucketName
	 * @param key
	 * @return ObjectMetadata
	 * @throws BulkStorageException
	 */
	public ObjectMetadata getObjectMetaData(String bucketName, String key)
			{
		try {
			return client.getObjectMetaData(bucketName, key);
		} catch (Exception er) {
			return null;
		}
	}

	/**
	 * 将一个文件分割下载
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return ReadResult
	 * @throws BulkStorageException
	 */
	public ReadResult read(String key, long start, long end)
			{
		try {
			ReadResult response = client.getObjectAsRangedStream(bucketName,
					key, start, end);
			return response;
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to read object from swift: KEY = " + key, er);
		}
	}

	/**
	 * 删除单个文件
	 * 
	 * @param key
	 * @throws BulkStorageException
	 */
	public void delete(String key) {
		try {
			client.deleteObject(bucketName, key);
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to remove object from swift: KEY = " + key, er);
		}
	}

	/**
	 * 删除一个bucket
	 * 
	 * @param bucketName
	 * @return boolean
	 * @throws BulkStorageException
	 */
	public boolean deleteBucket(FilesClient client, String bucketName)
			{
		boolean result = false;
		try {
			if (client.isBucketExist(bucketName)) {
				synchronized (LOCK) {
					if (client.isBucketExist(bucketName)) {
						result = client.deleteBucket(bucketName);
					}
				}
			}
			return result;
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to remove bucket from swift: bucketName = "
							+ bucketName, er);
		}
	}

	/**
	 * 验证bucket是否存在，如果不存在执行创建
	 * 
	 * @param client
	 * @param bucketName
	 * @throws BulkStorageException
	 */
	private void ensureBucket(FilesClient client, String bucketName)
			{
		try {
			if (!client.isBucketExist(bucketName)) {
				synchronized (LOCK) {
					if (!client.isBucketExist(bucketName)) {
						client.createBucket(bucketName);
					}
				}
			}
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to create bucket from swift: bucketName = "
							+ bucketName, er);
		}
	}

	/**
	 * 获取bucket的读写权限信息
	 * 
	 * @return AclMap AclMap里有两个键值：readAcl和writeAcl
	 * @throws BulkStorageException
	 */
	public Map<String, String> getBucketAcl() {
		try {
			BucketInfo bucketInfo = client.getContainerInfo(bucketName);
			return bucketInfo.getAcl();
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to get bucket ACL from swift", er);
		}
	}

	/**
	 * 添加匿名访问权限，所有人可以读bucket下的文件
	 * 
	 * @return boolean
	 * @throws BulkStorageException
	 */
	public boolean addLinkAcl() {
		try {
			return client.addReadAcl(bucketName, ".r:*,.rlistings");
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to add link bucket ACL to swift", er);
		}
	}

	/**
	 * 为bucket添加用户组角色
	 * 
	 * @param role
	 * @return boolean
	 * @throws BulkStorageException
	 */
	public boolean addReadAcl(String role) {
		try {
			return client.addReadAcl(bucketName, role);
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to add read bucket ACL to swift", er);
		}
	}

	/**
	 * 清空设置的读权限，采用默认的权限设置
	 * 
	 * @return boolean
	 * @throws BulkStorageException
	 */
	public boolean clearReadAcl() {
		try {
			return client.addReadAcl(bucketName, "");
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to clear read bucket ACL to swift", er);
		}
	}

	/**
	 * 为bucket添加用户组角色
	 * 
	 * @param role
	 * @return boolean
	 * @throws BulkStorageException
	 */
	public boolean addWriteAcl(String role) {
		try {
			return client.addWriteAcl(bucketName, role);
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to add write bucket ACL to swift", er);
		}
	}

	/**
	 * 清空设置的写权限，采用默认的权限设置
	 * 
	 * @return boolean
	 * @throws BulkStorageException
	 */
	public boolean clearWriteAcl() {
		try {
			return client.addWriteAcl(bucketName, "");
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to clear write bucket ACL to swift", er);
		}
	}

	/**
	 * 获取认证服务器中用户组角色列表
	 * 
	 * @return ArrayList
	 * @throws BulkStorageException
	 */
	public ArrayList<String> listRoles() {
		try {
			return client.getRoles();
		} catch (Exception er) {
			throw PaasServerExceptionFactory.storageException(
					"failed to get role list from keystone", er);
		}
	}
	
	public static void main(String[] args){
		SwiftBulkStorage storage;
		try {
			storage = new SwiftBulkStorage("http://192.168.74.13:35357/", "http://192.168.74.11:8080/", "tenant", "swift", "swift", "test");
			System.out.println(storage.listRoles());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
